﻿using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Maticsoft.DBUtility;
using System.Data;
using JSTT_DATA_ENTITY.XMLDatas;
using TSComm.Utilities.Datas;
using System.Data.Entity.Validation;

namespace JSTT_DATA_ENTITY
{
    public sealed class JSTTDevDataManager
    {
        private static JSTTDevDataManager _devDataManager;
        private static readonly object _syncLock = new object();
        private int FlushDataCount = 4000;

       
       
        //private string _devIdsStr = "";

        /// <summary>
        /// 设备类型规则列表
        /// </summary>
        private List<TD_DATATYPE_FORMAT_RULES> dataTypeFormatRulesList;

        /// <summary>
        /// 设备数据列表
        /// </summary>
        private List<I_JSTT_DEV_DATA> jstt_dev_data_list = new List<I_JSTT_DEV_DATA>();

        private JSTTDevDataManager()
        {
            dataTypeFormatRulesList = TD_DATATYPE_FORMAT_RULES.GetAllRules();
            List<TD_DATATYPE_FORMAT_RULES> notValidFormatRules = new List<TD_DATATYPE_FORMAT_RULES>(); // 无效的FormatRule
            TruncateTalbe("TD_DATATABLE_COLUMNS_INFO");  // 删除TD_DATATABLE_COLUMNS_INFO表数据，重新生成

            foreach (TD_DATATYPE_FORMAT_RULES tdDatatypeFormatRules in dataTypeFormatRulesList)
            {
                // 加载XML
                try
                {
                    tdDatatypeFormatRules.ReLoadRuleXml();
                }
                catch (Exception e)
                {
                    JSTTLogger.Logger.ProcWarnLogger(String.Format("Appliation Error: {0},So it can't process DataType:{1}.",e.Message,tdDatatypeFormatRules.DF_RULE_NAME_CHS));
                    notValidFormatRules.Add(tdDatatypeFormatRules);
                    continue;
                }

                // 将DataRule Format的XML信息更新到TD_DATATABLE_COLUMNS_INFO中.
                if (tdDatatypeFormatRules.FormatDataRule != null)
                {
                    int devtypeId = tdDatatypeFormatRules.DF_RULE_TYPE;
                    string ruleHistoryTableName = tdDatatypeFormatRules.FormatDataRule.JSTT_RuleTableInfo.HistoryTableName;
                    string ruleCurrencyTableName = tdDatatypeFormatRules.FormatDataRule.JSTT_RuleTableInfo.CurrencyTableName;
 
                    JSTT_DATA_ENTITY.JSTT_DBSEntities jsttEntity = new JSTT_DBSEntities();

                    foreach (DataRuleTableColumn drColumn in tdDatatypeFormatRules.FormatDataRule.JSTT_RuleTableInfo.TableColumns)
                    {
                        TD_DATATABLE_COLUMNS_INFO td_database_column_info = new TD_DATATABLE_COLUMNS_INFO();
                        td_database_column_info.TD_DEVICETYPE_ID = Convert.ToInt16(devtypeId);
                        td_database_column_info.TD_JSTT_HISTORY_TABLE_NAME = ruleHistoryTableName;
                        td_database_column_info.TD_JSTT_CURRENCY_TABLE_NAME = ruleCurrencyTableName;
                        td_database_column_info.COLUMN_NAME = drColumn.Name;
                        td_database_column_info.COLUMN_NAME_CHS = drColumn.DisplayName;
                        td_database_column_info.COLUMN_DATATYPE = drColumn.ColumnType;
                        td_database_column_info.COLUMN_ITEMUNITNAME = drColumn.ItemUnitName;
                        var o = tdDatatypeFormatRules.FormatDataRule.JSTT_RuleDataItems.RuleItems.Where(t => t.ItemProperty.DefineColumnName == drColumn.Name);
                        if (o != null && o.Count() > 0)
                        {
                            var ruleItem = o.First();
                            if (!ruleItem.ItemProperty.IsDefineColumn)
                            {
                                continue;
                            }
                            td_database_column_info.COLUMN_MAXVAL = ruleItem.ItemProperty.MaxValue;
                            td_database_column_info.COLUMN_MINVAL = ruleItem.ItemProperty.MinValue;
                        }
                        td_database_column_info.loaded_date = System.DateTime.Now;
                        jsttEntity.TD_DATATABLE_COLUMNS_INFO.Add(td_database_column_info);
                    }
                    try
                    {
                        jsttEntity.SaveChanges();
                    }
                    catch (DbEntityValidationException dbEx)
                    {
                        JSTTLogger.Logger.ProcWarnLogger(String.Format("Appliation Error: {0}.", dbEx.Message));
                    }
                    
                }
            }
            // 删除无效的FormatRule.
            notValidFormatRules.ForEach(t => dataTypeFormatRulesList.Remove(t));
            // 在构造函数中，加载各个设备类型的规则数据
        }


        public static void InsertReceiveLog(string ipaddress, int ipport, string data, int dataLength, string dtu, string type)
        {
            SqlParameter[] sqlParams = new SqlParameter[]
            {
                new SqlParameter("recv_datetime",System.DateTime.Now),
                new SqlParameter("recv_ipaddress",ipaddress),
                new SqlParameter("recv_port",ipport),
                new SqlParameter("recv_datalength",dataLength),
                new SqlParameter("recv_dtu",dtu),
                new SqlParameter("recv_datas",data),
                new SqlParameter("rev_sed_type",type)
 
            };
            DbHelperSQL.ExecuteSql("Insert into TF_RECIVE_DATAS_LOG" +
                                   "(RECEIVE_DATETIME,RECEIVE_IPADDRESS,RECEIVE_PORT,RECEIVE_DATA_LENGTH,RECEIVE_DTU,RECEIVE_DATAS,REV_SED_TYPE) " +
                                   "values(@recv_datetime,@recv_ipaddress,@recv_port,@recv_datalength,@recv_dtu,@recv_datas,@rev_sed_type);", sqlParams);
        }

        /// <summary>
        /// 判断数据类型
        /// </summary>
        /// <returns></returns>
        public TD_DATATYPE_FORMAT_RULES JudgeDataBelongDataType(byte[] bMessageBody)
        {
            foreach (TD_DATATYPE_FORMAT_RULES tdDatatypeFormatRules in dataTypeFormatRulesList)
            {
                if (tdDatatypeFormatRules.IsDataMatchThisType(bMessageBody))
                {
                    return tdDatatypeFormatRules;
                }
            }
            return null;
        }

        

        /// <summary>
        /// 获得对象实例
        /// </summary>
        /// <returns></returns>
        public static JSTTDevDataManager Instance()
        {
            if (_devDataManager == null)
            {
                lock (_syncLock)
                {
                    if (_devDataManager == null)
                    {
                        _devDataManager = new JSTTDevDataManager();
                    }
                }
            }
            return _devDataManager;
        }

        /// <summary>
        /// 获得缓存列表中所有设备IDs,默认以逗号分隔(,)
        /// </summary>
        /// <returns></returns>
        public string GetStroeDevIds(string splitStr=",")
        {
            string devIds = "";
            if (jstt_dev_data_list.Select(t => t.JST_DEVICE_ID).Count() <= 0)
                return "0";
            
            List<int> devIdList = jstt_dev_data_list.Select(t => t.JST_DEVICE_ID).ToList();
            devIdList.ForEach(t => devIds += t.ToString() + splitStr);
            if (devIds.Length > 1 && devIds[devIds.Length - 1] == ',')
                devIds = devIds.Substring(0, devIds.Length - 1);
            //devIds.Remove(devIds.Length - 1);

            return devIds;
        }

        /// <summary>
        /// 加入设备数据到列表中
        /// </summary>
        public void Add_JSTTDeviceData(I_JSTT_DEV_DATA dev_data)
        {
            lock (_syncLock)
            {
                if (dev_data != null)
                {
                    // 检查数据中是否存在相同的DevId,如果存在，则将原有的设备数据去除，使用新的数据
                    jstt_dev_data_list.RemoveAll(t => t.JST_DEVICE_ID == dev_data.JST_DEVICE_ID);

                    // 将新的设备数据加入到列表中
                    jstt_dev_data_list.Add(dev_data);
                }
            }
        }

        public int GetDevDataListCount()
        {
            return jstt_dev_data_list.Count;
        }

        /// <summary>
        /// 刷新数据
        /// </summary>
        public void FlushDevDataList()
        {
            lock (_syncLock)
            {
                //if (jstt_dev_data_list.Count <= 0)
                //    return;

                try
                {
                    // 更新状态数据表,状态数据表更新成功后，更新各个存储数据表
                    if (UpdateDeviceStatus())
                    {


                        // 更新数据表（TF_DATAS_TXX 和 TF_DATAS_CURRENCY_TXX)
                        foreach (TD_DATATYPE_FORMAT_RULES tdDatatypeFormatRules in dataTypeFormatRulesList)
                        {
                            // 更新Exception数据表
                            UpdateDeviceDataException(tdDatatypeFormatRules);
                            // 更新DeviceData,包括历史数据和当前数据表
                            UpdateDeviceData(tdDatatypeFormatRules);
                        }
                    }
                    jstt_dev_data_list.Clear();
                }
                catch (Exception e)
                {

                }
            }
        }

        #region 使用ADO.net 操作数据库
        /// <summary>
        /// 更新TD_DEVICETYPE_ECPT_SETTING数据
        /// </summary>
        /// <param name="td_datatype_format_rule"></param>
        /// <returns></returns>
        private bool UpdateDeviceDataException(TD_DATATYPE_FORMAT_RULES td_datatype_format_rule)
        {
            if (td_datatype_format_rule.FormatDataRule == null)
                return false;

            // 条件1是表示取得有异常的数据，条件2是表示设备数据的类型要匹配
            var q = from o in jstt_dev_data_list where (o.JST_EXPT_STATUS>0 
                        && o.JST_DATATYPE_ID==td_datatype_format_rule.DF_RULE_TYPE) 
                    select o;  // has exception

            if (q == null || q.Count()<=0)  // 没有此设备类型的数据
                return true;

            string queryFilter = string.Format(" JST_DEVICE_ID={0}",0);
            const string exptTableName = "TF_JSTT_DATAS_EXCEPTION";
            DataTable dt = GetDataTableByFilter(exptTableName, queryFilter);

            DataRuleItem dri = td_datatype_format_rule.FormatDataRule.GetDataRuleItemByColumName("JST_EXPT_STATUS");
            int expt_byteStart = dri.ByteStart;

            foreach (I_JSTT_DEV_DATA jsttDevData in q)
            {
                // 删除掉对应的DeviceID,因为只对应一个表，不能一起删除
                DeleteDataFromTableByIds(exptTableName, string.Format(" JST_DEVICE_ID={0}",jsttDevData.JST_DEVICE_ID));
                foreach (ExptItem exptItem in td_datatype_format_rule.FormatDataRule.JSTT_EXPTDataItems.ExptItems)
                {
                    try
                    {
                        byte[] columnDatas = TSComm.Utilities.Datas.ByteHelper.SplitByteDataLH(jsttDevData.DataBody,
                                 exptItem.ByteStart, exptItem.ByteLength, exptItem.BitStart, exptItem.BitLength);

                        if (columnDatas.ToShortH() > 0) // 存在异常
                        {
                            DataRow dr = dt.NewRow();
                            dr["TD_ID"] = 0;
                            dr["JST_DEVICE_ID"] = jsttDevData.JST_DEVICE_ID;
                            dr["JST_DEVICE_SIM"] = jsttDevData.JST_DEVICE_SIM;
                            //dr["JST_EXPT_STATUS"] = jsttDevData.JST_EXPT_STATUS;
                            dr["JST_DATE"] = dr["loaded_date"] = jsttDevData.JST_DATE;
                            int expt_byteStartNow = exptItem.ByteStart;
                            // 如果ExptItem中的ByteStart>DataItem中的ByteStart，说明是第二个字节的异常，BIT_EXPT_INDEX需要加8
                            dr["BIT_EXPT_INDEX"] = exptItem.BitStart + (expt_byteStartNow - expt_byteStart) * 8;
                            dr["BIT_EXPT_DESCRIPTION"] = exptItem.ItemRuleName;
                            dt.Rows.Add(dr);
                        }
                    }
                    catch (Exception e)
                    {
                        JSTTLogger.Logger.ProcWarnLogger(String.Format("UpdateDeviceDataException For Device {0} Failed:{1}", this.GetStroeDevIds(), e.Message));
                        return false;
                    }
                }
            }
            try
            {
                BulkInsertData(exptTableName, dt);
            }
            catch (Exception e)
            {
                JSTTLogger.Logger.ProcWarnLogger(String.Format("UpdateDeviceDataException For Device {0} Failed:{1}", this.GetStroeDevIds(), e.Message));
                return false;
            }
            
            return true;
        }

        /// <summary>
        /// 根据设备类型，更新Table数据。
        /// </summary>
        /// <param name="td_datatype_format_rule"></param>
        /// <returns></returns>
        private bool UpdateDeviceData(TD_DATATYPE_FORMAT_RULES td_datatype_format_rule)
        {
            if (td_datatype_format_rule.FormatDataRule == null)
                return false;

            // DataTable生成
            string df_rule_table_store = td_datatype_format_rule.DF_RULE_TABLE_STORE;
            string df_rule_curr_table_stroe = td_datatype_format_rule.DF_RULE_CURR_TABLE_STORE;
            string queryFilter = string.Format(" JST_DEVICE_ID in ({0})", this.GetStroeDevIds());
            string tableName = df_rule_curr_table_stroe;

            // 先从Currency表中删除数据，然后获得一个空的DataTable
            DeleteDataFromTableByIds(tableName, queryFilter);
            DataTable dt = GetDataTableByFilter(tableName, queryFilter);

            // 找到需要更新列表中所有符合此设备类型的数据
            var q = from o in jstt_dev_data_list where (o.JST_DATATYPE_ID == td_datatype_format_rule.DF_RULE_TYPE) select o;  // not in
            if (q == null || q.Count()<=0)
                return true;

            foreach (I_JSTT_DEV_DATA jsttDevData in q)
            {
                DataRow dr = dt.NewRow();
                //TD_ID，JST_DEVICE_ID，JST_DEVICE_SIM,JST_DATE，loaded_date固定数据列
                dr["TD_ID"] = 0;
                dr["JST_DEVICE_ID"] = jsttDevData.JST_DEVICE_ID;
                dr["JST_DEVICE_SIM"] = jsttDevData.JST_DEVICE_SIM;
                dr["JST_DEVTYPE_ID"] = jsttDevData.JST_DATATYPE_ID;
                dr["JST_EXPT_STATUS"] = jsttDevData.JST_EXPT_STATUS;
                dr["JST_DATE"] = dr["loaded_date"] = jsttDevData.JST_DATE;

                try
                {
                    // 其它列赋值
                    foreach (DataRuleTableColumn dtc in td_datatype_format_rule.FormatDataRule.JSTT_RuleTableInfo.TableColumns)
                    {
                        // 排除掉TD_ID，JST_DEVICE_ID，JST_DATE，loaded_date列
                        if (dtc.ColumnIsKey == "false")
                        {
                            DataRuleItem dri = td_datatype_format_rule.FormatDataRule.GetDataRuleItemByColumName(dtc.Name);
                            if (dri != null)
                            {
                                byte[] columnDatas = TSComm.Utilities.Datas.ByteHelper.SplitByteDataLH(jsttDevData.DataBody,
                                    dri.ByteStart, dri.ByteLength, dri.BitStart, dri.BitLength);
                                // 根据类型和长度，生成对应的数据
                                dr[dtc.Name] = TSComm.Utilities.Datas.ByteHelper.ConvertByteData(columnDatas, dtc.ColumnType, dtc.TypeLength, dri.ItemProperty.AmpFactor);
                            }
                        }
                    }
                    dt.Rows.Add(dr);
                }
                catch (Exception e)
                {
                    JSTTLogger.Logger.ProcWarnLogger(String.Format("UpdateDeviceData For Device {0} Failed:{1}", this.GetStroeDevIds(), e.Message));
                    return false;
                }
            }
            try
            {
                BulkInsertData(df_rule_curr_table_stroe, dt);
                BulkInsertData(df_rule_table_store, dt);
            }
            catch (Exception e)
            {
                JSTTLogger.Logger.ProcWarnLogger(String.Format("UpdateDeviceData For Device {0} Failed:{1}", this.GetStroeDevIds(), e.Message));
                return false;
            }

            return true;
        }

        /// <summary>
        /// 更新DeviceStaus
        /// </summary>
        /// <returns></returns>
        private bool UpdateDeviceStatus()
        {
            try
            {
                string queryFilter = string.Format(" DS_DEVICE_ID in ({0})", this.GetStroeDevIds());
                string tableName = "TF_DEVICE_STATUS";

                // 将离线数据更新(不包括当前设备数据）
                string updateDeviceStatusOfflineSQL = String.Format(@"UPDATE TF_DEVICE_STATUS
                            SET DS_STSTUS_CHARGE_TIME=GETDATE(),DS_CURRENT_STATUS=3,DS_ONLINE=2
                            WHERE DATEDIFF(hh,DS_UPDATE_TIME,GETDATE())>5 and DS_DEVICE_ID not in ({0}) ",this.GetStroeDevIds());
                DbHelperSQL.ExecuteSql(updateDeviceStatusOfflineSQL);

                if (jstt_dev_data_list.Count <= 0)
                    return false;


                //// 1. 所有在TD_DEVICE_STATUS数据表中存在的Device
                DataTable dt = GetDataTableByFilter(tableName, queryFilter);  // Table中获得的数据，都是这次需要更新的数据
                List<int> existDevIds = new List<int>();
                foreach (DataRow dr in dt.Rows) 
                {
                    existDevIds.Add(Convert.ToInt32(dr["DS_DEVICE_ID"]));

                    I_JSTT_DEV_DATA dev_data_object =
                        jstt_dev_data_list.Where(t => t.JST_DEVICE_ID == Convert.ToInt32(dr["DS_DEVICE_ID"])).First();

                    // 如果DEVICE_STATUS表中的更新日期 > 最新收到的设备数据日期，视为无效数据
                    if (DateTime.Compare(Convert.ToDateTime(dr["DS_UPDATE_TIME"]), dev_data_object.JST_DATE) > 0)
                    {
                        continue;
                    }
                    // DS_STSTUS_CHARGE_TIME : 数据的状态CHARGE.例如由设备正常->设备异常之间的CHARGE,保存了这个CHARGE点的时间。
                    int sts_now = dev_data_object.JST_EXPT_STATUS == 0 ? 1 : 2;
                    int sts_intable = Convert.ToInt32(dr["DS_CURRENT_STATUS"]);
                    if (sts_now!=sts_intable)
                        dr["DS_STSTUS_CHARGE_TIME"] = dev_data_object.JST_DATE; 
                    dr["loaded_date"] = System.DateTime.Now;

                    dr["DS_DTU_SIM"] = dev_data_object.JST_DEVICE_SIM;
                    dr["DS_EXCEPTION_COUNT"] = Convert.ToInt32(dr["DS_EXCEPTION_COUNT"])+(dev_data_object.JST_EXPT_STATUS==0 ? 0 : 1);
                    if (sts_intable == 3) // expired
                    {
                        dr["DS_POWERON_TIME"] = dev_data_object.JST_DATE;
                        dr["DS_EXCEPTION_COUNT"] = dev_data_object.JST_EXPT_STATUS==0 ? 0 : 1;
                    }
                    dr["DS_UPDATE_TIME"] = dev_data_object.JST_DATE;
                    dr["DS_STSUS_CODE"] = dev_data_object.JST_EXPT_STATUS;
                    dr["DS_ONLINE"] = 1;
                    dr["DS_UPDATER"] = "DEVICE_AUTO";
                    dr["DS_CURRENT_STATUS"] = dev_data_object.JST_EXPT_STATUS==0 ? 1 :2;
                }

                // 2. 找到不存在于Table中的dev_data_object
                var q = from o in jstt_dev_data_list where !(existDevIds.Contains(o.JST_DEVICE_ID)) select o;  // not in
                if (q != null)
                {
                    foreach (I_JSTT_DEV_DATA jsttDevData in q)
                    {
                        DataRow dr = dt.NewRow();
                        dr["loaded_date"] = System.DateTime.Now;

                        dr["DS_DEVICE_ID"] = jsttDevData.JST_DEVICE_ID;
                        dr["DS_LOADED_TIME"] = jsttDevData.JST_DATE;
                        dr["DS_POWERON_TIME"] = jsttDevData.JST_DATE;
                        dr["DS_DTU_SIM"] = jsttDevData.JST_DEVICE_SIM;
                        dr["DS_STSTUS_CHARGE_TIME"] = jsttDevData.JST_DATE;
                        dr["DS_UPDATE_TIME"] = jsttDevData.JST_DATE;
                        dr["DS_STSUS_CODE"] = jsttDevData.JST_EXPT_STATUS;
                        dr["DS_ONLINE"] = 1;
                        dr["DS_UPDATER"] = "DEVICE_AUTO";
                        dr["DS_CURRENT_STATUS"] = jsttDevData.JST_EXPT_STATUS <= 1 ? (byte)1 : (byte)2;
                        dr["DS_EXCEPTION_COUNT"] = jsttDevData.JST_EXPT_STATUS <= 1 ? 0 : 1;
                        dt.Rows.Add(dr);
                    }
                }

                // 3. 删除设备的旧数据
                DeleteDataFromTableByIds(tableName, queryFilter);

                // 4. 批量更新新的数据
           
                BulkInsertData(tableName, dt);
                

                // 5. 更新过期数据
                // TO DO...

                return true;
            }
            catch (Exception e)
            {
                JSTTLogger.Logger.ProcWarnLogger(String.Format("UpdateDeviceStatus For Device {0} Failed:{1}", this.GetStroeDevIds(), e.Message));
                return false;
            }
            
        }


        /// <summary>
        /// 批量插入数据
        /// </summary>
        /// <param name="tableName"></param>
        /// <param name="dt"></param>
        private void BulkInsertData(string tableName, DataTable dt)
        {
            if (dt == null || dt.Rows.Count <= 0)
                return;

            SqlBulkCopy sqlBulkCopy = new SqlBulkCopy(DbHelperSQL.connectionString);
            sqlBulkCopy.DestinationTableName = tableName;
            foreach (DataColumn dc in dt.Columns)
            {
                sqlBulkCopy.ColumnMappings.Add(dc.ColumnName, dc.ColumnName);
            }
            try
            {
                sqlBulkCopy.WriteToServer(dt);
                sqlBulkCopy.Close();
            }
            catch (Exception e)
            {
                throw e;
            }
            
        }

        private int DeleteDataFromTableByIds(string tableName, string strWhere)
        {
            StringBuilder strSql = new StringBuilder();
            strSql.Append("delete from " + tableName + " where " + strWhere);
            return DbHelperSQL.ExecuteSql(strSql.ToString());
        }

        private int TruncateTalbe(string tableName)
        {
            string sql = "truncate table " + tableName;
            return DbHelperSQL.ExecuteSql(sql);
        }

        private DataTable GetDataTableByFilter(string tableName, string strWhere, string filedOrder = "")
        {
            StringBuilder strSql = new StringBuilder();
            strSql.Append("select * FROM "+tableName+" ");
            if (strWhere.Trim() != "")
            {
                strSql.Append(" where " + strWhere);
            }
            if (filedOrder.Trim() != "")
            {
                strSql.Append(" order by " + filedOrder);
            }
            return DbHelperSQL.Query(strSql.ToString()).Tables[0];
        }


             #endregion
    }
}
